// Copyright (c) 2011 Blue Onion Software, All rights reserved
using System.IO;
using System.Threading;

namespace BlueOnionSoftware.Bloget
{
    internal static class FileUtilities
    {
        internal static void WriteFile(string fileName, Stream stream)
        {
            Throw.IfNullOrEmpty(fileName, "fileName");
            Throw.IfNull(stream, "stream");

            CreateBackupFile(fileName);

            using (var fileWriter = OpenCreate(fileName))
            {
                CopyStream(fileWriter, stream);
            }
        }

        private static void CopyStream(Stream destination, Stream source)
        {
            int count;
            var buffer = new byte[16*1024];

            while ((count = source.Read(buffer, 0, buffer.Length)) != 0)
            {
                destination.Write(buffer, 0, count);
            }
        }

        private static void CreateBackupFile(string file)
        {
            if (File.Exists(file))
            {
                var retries = 10;
                var backupFile = file + ".bak";

                for (;;)
                {
                    try
                    {
                        File.Copy(file, backupFile, true);
                        break;
                    }

                    catch (IOException)
                    {
                        if (--retries <= 0)
                        {
                            throw;
                        }

                        Thread.Sleep(100);
                    }
                }
            }
        }

        internal static FileStream OpenCreate(string fileName)
        {
            return OpenFile(fileName, name => new FileStream(name, FileMode.Create, FileAccess.Write, FileShare.None));
        }

        internal static FileStream OpenRead(string fileName)
        {
            return OpenFile(fileName, name => new FileStream(name, FileMode.Open, FileAccess.Read, FileShare.Read));
        }

        internal static FileStream OpenAppend(string fileName)
        {
            return OpenFile(fileName, name => new FileStream(name, FileMode.Append, FileAccess.Write, FileShare.None));
        }

        private delegate FileStream OpenFileHandler(string fileName);

        private static FileStream OpenFile(string fileName, OpenFileHandler openFileHandler)
        {
            var retries = 10;
            FileStream file;

            for (;;)
            {
                try
                {
                    file = openFileHandler(fileName);
                    break;
                }

                    // FileNotFoundException, DirectoryNotFoundException and PathTooLongException are
                    // all derived from IOException

                catch (FileNotFoundException)
                {
                    throw;
                }

                catch (DirectoryNotFoundException)
                {
                    throw;
                }

                catch (PathTooLongException)
                {
                    throw;
                }

                catch (IOException)
                {
                    if (--retries <= 0)
                    {
                        throw;
                    }

                    Thread.Sleep(100);
                }
            }

            return file;
        }
    }
}